﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace V32Assembler
{
    public static class Util
    {
        public static byte[] ToBytes(this uint intVal)
        {
            return new byte[]
            {
                (byte)(intVal >> 0),
                (byte)(intVal >> 8),
                (byte)(intVal >> 16),
                (byte)(intVal >> 24)
            };
        }

        public static BaseNumber ParseBaseNumber(this string str, Dictionary<string, int> labelLines)
        {
            string inputStr = str;

            if (str.StartsWith("sizeof(") && str.EndsWith(")"))
            {
                str = str.Substring("sizeof(".Length);
                str = str.Remove(str.Length - 1).Trim();
                Tuple<Argument, string> arg = SourceArg.FromString(str, labelLines);
                if (arg == null) return null;
                return new BaseNumber(arg.Item1.GetBaseArgSize());
            }

            uint val;
            // format: 0[x/b/o][#]
            if (str.ToLower().StartsWith("0x") || str.ToLower().StartsWith("0b") || str.ToLower().StartsWith("0o"))
            {
                // parse hex, binary, or octal number.
                if (str.Length == 2) return null;//throw new Error("'" + str + "' is not a number.");
                else
                {
                    try
                    {
                        val = Convert.ToUInt32(str, str.ToLower()[1] == 'x' ? 16 : (str.ToLower()[1] == 'b' ? 2 : 8));
                        return new BaseNumber(val);
                    }
                    catch (Exception)
                    {
                        return null;
                        //throw new Error("'" + str + "' is not a number.");
                    }
                }
            }
            else
            {
                // try to parse decimal number.
                if (uint.TryParse(str, out val))
                    return new BaseNumber(val);

                //throw new Error("Could not parse '" + inputStr + "' as an offset number.");
                return null;
            }
        }
        public static OffsetNumber ParseOffsetNumber(this string str, Dictionary<string, int> labelLines)
        {
            string inputStr = str;

            if (str.StartsWith("sizeof(") && str.EndsWith(")"))
            {
                str = str.Substring("sizeof(".Length);
                str = str.Remove(str.Length - 1).Trim();
                Tuple<Argument, string> arg = SourceArg.FromString(str, labelLines);
                if (arg == null) return null;
                return new OffsetNumber(arg.Item1.GetBaseArgSize(), true);
            }

            uint val;
            // format: 0[x/b/o][#]+[bw]
            if (str.ToLower().StartsWith("0x") || str.ToLower().StartsWith("0b") || str.ToLower().StartsWith("0o"))
            {
                // parse hex, binary, or octal number.
                if (str.Length == 2) return null;//throw new Error("'" + str + "' is not a number.");
                else
                {
                    try
                    {
                        bool forceSize = false;
                        bool isByte = false;
                        if (str.ToLower().EndsWith("b") || str.ToLower().EndsWith("w"))
                        {
                            forceSize = true;
                            isByte = str.ToLower().EndsWith("b");
                            str = str.Remove(str.Length - 1);
                        }
                        val = Convert.ToUInt32(str, str.ToLower()[1] == 'x' ? 16 : (str.ToLower()[1] == 'b' ? 2 : 8));
                        if (forceSize && isByte && val >= 256)
                            throw new Error("A byte must be less than 256.");
                        if (!forceSize)
                            isByte = val < 256;
                        return new OffsetNumber(val, isByte);
                    }
                    catch (Error)
                    {
                        throw;
                    }
                    catch (Exception)
                    {
                        return null;//throw new Error("'" + str + "' is not a number.");
                    }
                }
            }
            else
            {
                // try to parse decimal number.
                bool forceSize = false;
                bool isByte = false;
                if (str.ToLower().EndsWith("b") || str.ToLower().EndsWith("w"))
                {
                    forceSize = true;
                    isByte = str.ToLower().EndsWith("b");
                    str = str.Remove(str.Length - 1);
                }
                if (uint.TryParse(str, out val))
                {
                    if (forceSize && isByte && val >= 256)
                        throw new Error("A byte must be less than 256.");
                    if (!forceSize)
                        isByte = val < 256;
                    return new OffsetNumber(val, isByte);
                }

                return null;//throw new Error("Could not parse '" + inputStr + "' as a base number.");
            }
        }

        public static string ParseString(this string str)
        {
            str = str.Trim();

            string inputStr = str;

            if (!str.StartsWith("\"")) throw new Error("A string must start with quotation marks (\").");

            // n, r, f, t, b, ", \
            string result = "";
            for (int i = 1; i < str.Length; i++)
            {
                if (str[i] == '\\')
                {
                    if (++i >= str.Length) throw new Error("Unescaped '\\' at the end of a string.");
                    switch (str[i])
                    {
                        case '\\': result += '\\'; break;
                        case '"': result += '"'; break;
                        case 'n': result += '\n'; break;
                        case 'r': result += '\r'; break;
                        case 'f': result += '\f'; break;
                        case 't': result += '\t'; break;
                        case 'b': result += '\b'; break;
                        default: throw new Error("Unknown escape character '\\" + str[i] + "'.");
                    }
                }
                else if (str[i] == '"')
                {
                    if (i != str.Length - 1)
                        throw new Error("Could not parse '" + inputStr + "' as a string.");
                    else break;
                }
                else result += str[i];
            }

            return result;
        }

        public static List<T> ShallowCopy<T>(this IEnumerable<T> items)
        {
            List<T> list = new List<T>();
            list.AddRange(items);
            return list;
        }
    }
}
